home *** CD-ROM | disk | FTP | other *** search
/ The X-Philes (2nd Revision) / The X-Philes Number 1 (1995).iso / xphiles / hp48_2 / mpe_v1_0.doc < prev    next >
Internet Message Format  |  1995-03-23  |  43KB

  1. From comp.sys.handhelds Fri May 24 22:30:22 1991
  2. Path: seq!ecsgate!mcnc!taco!lll-winken!elroy.jpl.nasa.gov!swrinde!zaphod.mps.ohio-state.edu!wuarchive!udel!haven.umd.edu!decuac!pa.dec.com!nntpd.lkg.dec.com!ryn.mro4.dec.com!pinbot.enet.dec.com!ervin
  3. From: ervin@pinbot.enet.dec.com (Joseph James Ervin)
  4. Newsgroups: comp.sys.handhelds
  5. Subject: HP48 MPE V1.0 User's Guide
  6. Message-ID: <4842@ryn.mro4.dec.com>
  7. Date: 15 May 91 19:15:28 GMT
  8. Sender: guest@ryn.mro4.dec.com
  9. Reply-To: ervin@pinbot.enet.dec.com (Joseph James Ervin)
  10. Organization: Digital Equipment Corporation
  11. Lines: 894
  12.  
  13.  
  14.  
  15.  
  16.                                       MPE
  17.  
  18.  
  19.                 A Multi-Programming Environment for the HP-48SX
  20.  
  21.                                        by
  22.  
  23.                                    Joe Ervin
  24.  
  25.  
  26.  
  27.  
  28.  
  29.  
  30.  
  31.  
  32.      1  PURPOSE
  33.  
  34.      The purpose of this document is to describe the function  and  use  of
  35.      the Multi-Programming Environment, written by Joe Ervin.
  36.  
  37.  
  38.  
  39.      2  INTRODUCTION
  40.  
  41.      The Multi-Programming Environment (MPE) is a set of  machine  language
  42.      routines,   data  structures,  and  Star  macros  which  implement  an
  43.      environment  whereby  the  software  developer  can   easily   program
  44.      concurrent  tasks.  The immediate use of MPE is for graphics animation
  45.      such as in games  programming,  where  multiple  objects  need  to  be
  46.      animated on the screen at the same time (bullets, explosions, pac-men,
  47.      etc.), however an environment such as this can be extremely useful for
  48.      periodic  keyboard  polling  and  other events that need to occur from
  49.      time to time.
  50.  
  51.      This distribution of MPE includes the STAR source code for MPE version
  52.      1.0,  as  well as a set of process definitions which comprise a simple
  53.      interactive graphics demo, showing the  main  features  of  MPE.   The
  54.      source  code  and  the uuencoded file have been posted separately.  If
  55.      you do not have access to either of these documents, please send  mail
  56.      to "ervin@pinbot.enet.dec.com" and I will mail them to you.
  57.  
  58.  
  59.  
  60.      3  USER'S GUIDE
  61.  
  62.      The remainder of this document comprises the MPE User's  Guide.   This
  63.      guide  discusses  the basic structure of a process under MPE, and then
  64.      goes on to describe the various routines and macros and how  they  are
  65.      used.   These  routines and macros allow the user to do such things as
  66.      process scheduling, process entry-point redirection, and  interprocess
  67.      communication.   Finally,  the example set of processes which comprise
  68.      the demo are described at the end of the user's guide.
  69.  
  70.                                                                      Page 2
  71.  
  72.  
  73.      3.1  Process Structure
  74.  
  75.      The basic idea behind MPE is that the programmer defines one  or  more
  76.      "processes"  which are activated, or invoked by MPE.  MPE keeps a list
  77.      of all processes which are waiting to run, and runs the  processes  as
  78.      they  come  due.   As  each  process  is  run,  it is deleted from the
  79.      run-list, thus  requiring  processes  to  be  rescheduled  after  each
  80.      invocation if repeated execution is desired.
  81.  
  82.      The manner in which MPE handles processes is as follows.  The run-list
  83.      contains  a  set  of process ID/time pairs, where the "ID" is an 8-bit
  84.      process ID, and the "time" is the absolute time (in  clock  ticks)  at
  85.      which  that  process  should be run.  The clock ticks referred to here
  86.      are the same "ticks" as those  that  are  associated  with  the  TICKS
  87.      command  described  in  the  HP48 owners manual.  There are 8192 clock
  88.      ticks  per  second.   The  RUN_LIST  is  a  data  structure  used  and
  89.      maintained  by  MPE.  The casual programmer need not be concerned with
  90.      its workings.
  91.  
  92.      The association between processes and IDs is implied through  the  use
  93.      of global labels in the assembler code for the processes.  MPE expects
  94.      that each process will have at its beginning a label of the form:
  95.  
  96.      PROCESSx_INIT:
  97.  
  98.      ...where the x in PROCESSx_INIT is a decimal number from 1 to 255.   A
  99.      value of 0 may NOT be used as a process ID since the MPE reserves this
  100.      ID for the scheduler.
  101.  
  102.      The  portion  of  MPE  that  manages  the  run-list  is   called   the
  103.      "scheduler", and will be referred to as such for the remainder of this
  104.      document.  When an application written using MPE is first started, the
  105.      scheduler's  run-list is empty.  In order to activate the application,
  106.      MPE automatically schedules process #1 for immediate execution.  As  a
  107.      result,  the control flow will pass immediately to the code located at
  108.      PROCESS1_INIT:.
  109.  
  110.                                       NOTE
  111.  
  112.              This is the only time that a process will  be  invoked
  113.              automatically by the MPE.  It is the responsibility of
  114.              the programmer to  ensure  that  all  processes  which
  115.              comprise   the   application   are  scheduled  by  the
  116.              application itself.  Processes are scheduled by  using
  117.              the ADD_PROCESS routine, described below.
  118.  
  119.  
  120.      Initially, MPE defines the entry points of all  processes  to  be  the
  121.      PROCESSx_INIT  label  of each process.  In many applications, however,
  122.      it will be necessary for a process to execute its initialization  code
  123.      only  once;  the  first  time  the  process is activated.  In order to
  124.      facilitate the distinction between process initialization code and the
  125.      main  process  body,  MPE includes a macro called PROCESS_START, which
  126.      allows the process to redefine its entry point.
  127.  
  128.                                                                      Page 3
  129.  
  130.  
  131.      The PROCESS_START macro takes two parameters; the ID  of  the  process
  132.      whose  entry  point  is to be modified, and the label corresponding to
  133.      the desired entry point.  For example, Figure 1 shows process #1 using
  134.      the  PROCESS_START  macro to alter its entry point.  This example also
  135.      shows the use of several of the other facilities in MPE.  These  other
  136.      facilities will be described in later sections.
  137.  
  138.      ;**********************************************************************
  139.      PROCESS1_INIT:          ; The initial entry point for process #1.
  140.  
  141.              PROCESS_START 1, PROCESS1_CODE  
  142.                              ; Changes the entry point of process #1 to the 
  143.                              ; PROCESS1_CODE label for subsequent 
  144.                              ; invocations.
  145.              . 
  146.              (Initialization code for process #1.)
  147.              . 
  148.      PROCESS1_CODE:          ; The main body of process #1 starts here.  
  149.              . 
  150.              (Body of process #1)
  151.              .
  152.              SAVE_CONTEXT    
  153.              .       ; This causes MPE to save a "snapshot" of this 
  154.              .       ; process's register context (B, D, D0, D1, R0-R4) 
  155.              .       ; so that the next time this process is started, its 
  156.              .       ; registers will contain the same data as at the 
  157.              .       ; time SAVE_CONTEXT is called.  A and C are not saved.
  158.              .
  159.      RESCHEDULE:     ; Now we want to schedule this process to execute again
  160.                      ; in 1 second.   
  161.  
  162.              ADDR    CUR_TIME, D0    
  163.                      ; ADDR is a standard HP48 macro found in the HP48.STAR 
  164.                      ; macro library by Jan Brittenson. CUR_TIME is a global 
  165.                      ; MPE variable which holds a copy of the time 
  166.                      ; (in ticks) when this process was activated. 
  167.              MOVE.W  @D0, C
  168.              CLR.W   A
  169.              MOVE.P5 ^x1FFF, A
  170.              ADD.W   A, C    ; 
  171.  
  172.              ; CUR_TIME + ^x1FFF ticks = CUR_TIME + 1 sec.
  173.  
  174.              MOVE.P5 ^x1, A  ; We are rescheduling process #1. This would 
  175.                              ; be ^x2 for process #2, ^x3 for process #3, 
  176.                              ; etc..
  177.              CALL    ADD_PROCESS     ; Schedule process #1 for execution 
  178.                                      ; at 1 sec. from when the current 
  179.                                      ; invocation started.
  180.              JUMP    TO_SCHEDULER    ; Jump back to the scheduler.
  181.      ;**********************************************************************
  182.  
  183.  
  184.                                     Figure 1
  185.  
  186.                                                                      Page 4
  187.  
  188.  
  189.      In addition to the PROCESS_START macro, there is the CUR_PROCESS_START
  190.      macro,  which  is  very  similar  to  PROCESS_START,  but  with  small
  191.      differences.  The CUR_PROCESS_START macro works only  on  the  current
  192.      process.   The  CUR_PROCESS_START  macro  expects  that  the  user has
  193.      previously loaded the address corresponding to the new entry point for
  194.      the  current  process  into C.A.  Invoking the CUR_PROCESS_START macro
  195.      will then take that address in C.A and cause the  current  process  to
  196.      use  that  as  its  new entry point.  The ADDR macro in the HP48 macro
  197.      library by Jan Brittenson is useful for loading C.A with  the  address
  198.      of the new entry point.
  199.  
  200.      The main impetus for the CUR_PROCESS_START macro is to avoid the  need
  201.      of multiple IF_PROC/ENDIF_PROC constructs for code which may be shared
  202.      by many processes.  In games that require many objects on the  screen,
  203.      each  of which with its own process, it is conceivable that the number
  204.      of IF_PROC/ENDIF_PROC constructs required by the  PROCESS_START  macro
  205.      would be unwieldy.
  206.  
  207.  
  208.  
  209.      3.2  Process Context
  210.  
  211.      3.2.1  The SAVE_CONTEXT Routine
  212.  
  213.      One of the main functions of MPE is to ensure that  each  process  has
  214.      its  own processor context.  To do this, MPE provides the SAVE_CONTEXT
  215.      routine which automatically  saves  the  processor  registers  of  the
  216.      current process in a special data structure maintained by MPE for each
  217.      process.  The registers saved are B, D, D0, D1, and R0-R4.  Note  that
  218.      MPE makes no attempt to save Registers A and C between invocations.
  219.  
  220.      By calling the SAVE_CONTEXT routine, a process indicates to  MPE  that
  221.      the  current  contents of its registers should be used as the starting
  222.      contents when the process is next invoked.  It is  the  responsibility
  223.      of  the programmer to call SAVE_CONTEXT at an appropriate point in the
  224.      process when all the registers have the desired  contents.   MPE  does
  225.      not  provide a mechanism for saving or restoring individual registers.
  226.      The SAVE_CONTEXT  routine  alters  A  and  C,  but  leaves  all  other
  227.      processor registers intact.
  228.  
  229.      MPE also provides the SAVE_CONTEXT_BY_A routine which allows the  user
  230.      to  specify  the register context for any arbitrary process.  The user
  231.      provides a process ID in  register  A.B,  and  SAVE_CONTEXT_BY_A  will
  232.      write  the  current  register  context  into  the context storage area
  233.      maintained by MPE for that process.  Note that no  error  checking  is
  234.      done  on  the  ID  provided in A.B, so the user should be careful with
  235.      using this routine.
  236.  
  237.  
  238.  
  239.      3.2.2  The RESTORE_CONTEXT Routine.
  240.  
  241.      Whenever a process comes due and is about to be executed, MPE uses the
  242.      RESTORE_CONTEXT  routine  to  restore  the process's register context,
  243.      after  which  MPE  jumps  to   the   process's   entry   point.    The
  244.  
  245.                                                                      Page 5
  246.  
  247.  
  248.      RESTORE_CONTEXT  routine,  together  with the SAVE_CONTEXT routine can
  249.      also be useful to the  programmer  in  general.   The  RESTORE_CONTEXT
  250.      routine restores the register context of the process whose ID is given
  251.      in A.B.  Obviously, this routine effects all the processor registers.
  252.  
  253.      The RESTORE_CONTEXT routine can be useful to the  programmer  whenever
  254.      information  needs  to  be  shared between processes.  For example, in
  255.      some applications, it may be desirable  to  extract  information  that
  256.      another  process  is  keeping in one of its processor registers.  This
  257.      may be accomplished by using the RESTORE_CONTEXT  routine,  specifying
  258.      the other process ID in A.B.  Note that it is probably advisable for a
  259.      process to save  its  own  context  via  SAVE_CONTEXT  before  calling
  260.      RESTORE_CONTEXT to see the other process's registers.  That way, after
  261.      the extracted data has been used, the  process  can  restore  its  own
  262.      context if necessary and continue execution.
  263.  
  264.      Note that although the  SAVE_CONTEXT  routine  does  not  require  any
  265.      arguments, the RESTORE_CONTEXT routine requires the process ID in A.B.
  266.  
  267.  
  268.  
  269.      3.2.3  The CURRENT_CONTEXT_ID Variable
  270.  
  271.      In order to keep track of where to  save  register  context  when  the
  272.      SAVE_CONTEXT  routine  is  called,  MPE  maintains  a  variable called
  273.      CURRENT_CONTEXT_ID.  This is a one byte variable which contains a copy
  274.      of the current process's ID.  While this variable is primarily for use
  275.      by the SAVE_CONTEXT routine,  it  comes  in  very  handy  for  certain
  276.      applications.
  277.  
  278.      One useful action that involves  the  CURRENT_CONTEXT_ID  is  that  of
  279.      modifying  the  saved  state  of another process.  This can be done by
  280.      first saving the current process's context, then restoring  the  other
  281.      process's  context.   At this point, the current process has access to
  282.      all the register context of the other  process.   By  modifying  these
  283.      contents,  changing  the  CURRENT_CONTEXT_ID  variable  to  the  other
  284.      process's ID, and then executing the SAVE_CONTEXT routine,  the  other
  285.      process's  context  can  be  overwritten.  The programmer must be very
  286.      careful to ensure that the CURRENT_CONTEXT_ID variable is returned  to
  287.      the  current  process's  ID  before  control  is  passed  back  to the
  288.      scheduler.
  289.  
  290.      Another useful technique involving the CURRENT_CONTEXT_ID variable  is
  291.      that  of  sharing  code among several processes.  This is discussed in
  292.      detail below.
  293.  
  294.      To make accessing the CURRENT_CONTEXT_ID variable easier, MPE includes
  295.      a routine called GET_CURRENT_ID, which when called returns the current
  296.      process ID into A.B.  This routine modifies the contents of A.
  297.  
  298.                                                                      Page 6
  299.  
  300.  
  301.      3.3  Scheduling Processes - The ADD_PROCESS Routine
  302.  
  303.      As stated above, it is necessary for the user's  code  to  handle  the
  304.      scheduling  of  all  processes,  except  for  the initial execution of
  305.      process #1  at  startup.   Processes  are  scheduled  by  use  of  the
  306.      ADD_PROCESS  routine.   This routine takes as its arguments a one byte
  307.      process ID in register A, and a 16-nibble  absolute  time  (ticks)  in
  308.      register  C.   ADD_PROCESS  then  schedules  the specified process for
  309.      execution at the specified time.
  310.  
  311.      The ADD_PROCESS routine modifies A, C, B, D, D0, D1, and R0, so it  is
  312.      recommended   that   this   routine   be   executed  only  during  the
  313.      initialization phase of a process, after the  process  has  saved  its
  314.      register  context via the SAVE_CONTEXT routine, or any other time when
  315.      the contents of these registers is not critical to the process.
  316.  
  317.  
  318.  
  319.      3.4  The CUR_TIME Variable
  320.  
  321.      In order to reschedule a process at a certain approximate interval  it
  322.      is necessary that the process have knowledge of the system time at the
  323.      point when the process was invoked.  MPE provides this information  in
  324.      the  CUR_TIME variable.  The CUR_TIME variable holds a 16 nibble value
  325.      representing the system time when the current process was invoked.  To
  326.      schedule  itself  on regular intervals, a process should read the time
  327.      value at CUR_TIME, add in a time  delay,  and  reschedule  itself  for
  328.      execution  at the resulting future time.  Figure 2 shows a typical use
  329.      of CUR_TIME.  In this example, process #3 is rescheduling  itself  for
  330.      CUR_TIME plus 1 second.  This causes process #3 to execute on 1 second
  331.      intervals.
  332.  
  333.      ;********************************************************************
  334.      ; Reschedule process #3 for execution in 1 second.
  335.  
  336.              ADDR    CUR_TIME, D0            ; Point D0 to the CUR_TIME
  337.                                              ; variable.
  338.              MOVE.W  @D0, C                  ; C.W now contains the time 
  339.                                              ; when this process was
  340.                                              ; invoked by the scheduler.
  341.              MOVE.P5 ^x1FFF, A       ; ^x1FFF = ^d8192 = 1 second.
  342.              ADD.W   A, C
  343.              MOVE.P2 ^d3
  344.              CALL    ADD_PROCESS     ; Add the process to the RUN_LIST.
  345.              JUMP    TO_SCHEDULER    ; Return control to the scheduler.
  346.  
  347.      ;********************************************************************
  348.  
  349.  
  350.                                     Figure 2
  351.  
  352.  
  353.      The CUR_TIME variable can be accessed by using the ADDR macro from the
  354.      HP48 macro library by Jan Brittenson.  This method also works well for
  355.      accessing any other MPE variables.  The CUR_TIME variable can also  be
  356.  
  357.                                                                      Page 7
  358.  
  359.  
  360.      accessed  by  calling  the  GET_CUR_TIME  routine,  which  returns the
  361.      CUR_TIME variable in C.W.  This routine modifies register A.
  362.  
  363.  
  364.  
  365.      3.5  Returning Control To The Scheduler
  366.  
  367.      After a  process  has  completed  its  useful  work  for  the  current
  368.      invocation, it needs to save its register context via the SAVE_CONTEXT
  369.      routine, and then reschedule itself if  desired.   Finally,  the  last
  370.      thing  a process does is return control to the scheduler by jumping to
  371.      the MPE label TO_SCHEDULER as follows:
  372.  
  373.              JUMP    TO_SCHEDULER    ; Returns control to the scheduler.
  374.  
  375.  
  376.      The user should be careful to use the JUMP instruction  here  and  NOT
  377.      the CALL instruction.
  378.  
  379.  
  380.  
  381.      3.6  Sharing Process Code
  382.  
  383.      In many  applications,  several  processes  may  be  needed  which  do
  384.      essentially   the   same  thing.   Consider  the  situation  where  an
  385.      application wants several processes which do very similar things, such
  386.      as  "bullets"  flying about on the screen as is common in many popular
  387.      arcade  games.   Since  each  of  these  processes  would  be  running
  388.      essentially  identical  code,  it  would be very useful if each of the
  389.      "bullet" processes could actually  share  the  same  code.   This  can
  390.      easily  be  accomplished  by placing multiple PROCESSxINIT:  labels at
  391.      the top of the process.
  392.  
  393.      One problem that soon becomes apparent, however, is that  this  shared
  394.      code  must  inevitably  perform some actions that are specific to each
  395.      process.  It is therefore necessary that the shared code has a way  of
  396.      determining which process is currently executing.  One example of this
  397.      is that since these processes will need to reschedule themselves,  the
  398.      shared  code  must  have some way of rescheduling the correct process.
  399.      By accessing the CURRENT_CONTEXT_ID, the shared code can determine the
  400.      ID of the process it is currently servicing.
  401.  
  402.      The CURRENT_CONTEXT_ID variable can be  accessed  by  using  the  ADDR
  403.      macro  from  the  HP48  macro  library  by Jan Brittenson, as is shown
  404.      below.  This method also  works  well  for  accessing  any  other  MPE
  405.      variables.
  406.  
  407.      ;********************************************************************
  408.      ; Check which process is currently running.
  409.  
  410.              ADDR    CURRENT_CONTEXT_ID, D0  ; Point D0 to the 
  411.                                              ; CURRENT_CONTEXT_ID variable.
  412.              MOVE.B  @D0, C                  ; C.B now contains the ID of the
  413.                                              ; current process.
  414.  
  415.  
  416.                                                                      Page 8
  417.  
  418.  
  419.      ;********************************************************************
  420.  
  421.  
  422.                                     Figure 3
  423.  
  424.  
  425.      Because of the usefulness of code-sharing, MPE contains a macro  which
  426.      makes  the  conditional execution of code based on the current process
  427.      ID very simple.
  428.  
  429.      The IF_PROC and ENDIF_PROC macros allow the programmer to easily cause
  430.      the execution of a specific section of code conditioned on the current
  431.      context ID.  The format of this structure is as follows:
  432.      ;********************************************************************
  433.  
  434.              IF_PROC n       ; where "n" is the process ID in question.
  435.                .
  436.                .
  437.                (ML code to be executed only if process #n is current.)
  438.                . 
  439.                . 
  440.              ENDIF_PROC
  441.      ;********************************************************************
  442.  
  443.  
  444.                                     Figure 4
  445.  
  446.  
  447.  
  448.                                       NOTE
  449.  
  450.              The user  should  be  aware  that  the  IF_PROC  macro
  451.              modifies registers A and C regardless of which process
  452.              ID is current.  For this  reason,  A  and  C  will  be
  453.              altered  whenever  IF_PROC  is  used.  The user should
  454.              therefore make no  attempt  to  pass  values  into  an
  455.              IF_PROC/ENDIF_PROC construct using registers A or C.
  456.  
  457.      As an example of when to use IF_PROC, consider a section of code which
  458.      is  shared by processes #1, #2 and #3.  Let us assume that the process
  459.      needs to reschedule itself after executing.  It is therefore necessary
  460.      that  the  code  pass  the  proper ID in register A to the ADD_PROCESS
  461.      routine, regardless of which process (#1, #2, or #3) is executing  the
  462.      code.   The  following  example  represents one way that this could be
  463.      accomplished.
  464.  
  465.  
  466.  
  467.                                                                      Page 9
  468.  
  469.  
  470.      ; *****************************************************************
  471.      ; Example of how to use IF_PROC.
  472.      ; This code assumes that the next desired run-time for this process
  473.      ; has been loaded into R0.
  474.  
  475.  
  476.              IF_PROC 3
  477.                MOVE.W        R0, C   ; Next time process should be run.
  478.                MOVE.P2       ^d3, A  ; ID for this process into A.B.
  479.                JUMP  PROCESS_SCHED           
  480.              ENDIF_PROC
  481.  
  482.              IF_PROC 4
  483.                MOVE.W        R0, C   ; Next time process should be run.
  484.                MOVE.P2       ^d4, A  ; ID for this process into A.B.
  485.                JUMP  PROCESS_SCHED
  486.              ENDIF_PROC
  487.  
  488.              IF_PROC 5
  489.                MOVE.W        R0, C   ; Next time process should be run.
  490.                MOVE.P2       ^d5, A  ; ID for this process into A.B.
  491.                JUMP  PROCESS_SCHED
  492.              ENDIF_PROC
  493.  
  494.      PROCESS_SCHED:  CALL    ADD_PROCESS     
  495.                                      ; Schedule the current process to 
  496.                                      ; run at the time given in C.   
  497.              JUMP    TO_SCHEDULER    ; Return control to the scheduler.
  498.      ; *****************************************************************
  499.  
  500.  
  501.                                     Figure 5
  502.  
  503.  
  504.  
  505.      3.7  Interprocess Communication
  506.  
  507.      In previous sections it was described how  interprocess  communication
  508.      could  be  done by modifying the latent process's context.  While this
  509.      is valid, it is a terribly inefficient means for communicating between
  510.      processes.   A  much  better  way is to use global variables which are
  511.      shared between processes.  Since processes are  never  interrupted  by
  512.      the  scheduler,  there  is no need to synchronize access of the shared
  513.      variables, so processes may pass information  back  and  forth  freely
  514.      using this scheme.
  515.  
  516.  
  517.  
  518.      3.8  Process Timing
  519.  
  520.      One issue that the programmer needs to be aware of when running  under
  521.      MPE  is that of process latency, particularly under heavy CPU loading,
  522.      and the effect that this latency has on process timing.
  523.  
  524.      Consider a scenario  where  many  processes  are  running  under  MPE.
  525.  
  526.                                                                     Page 10
  527.  
  528.  
  529.      Looking  at two of these processes, say #1 and #2, let us pretend that
  530.      process #1 always  schedules  itself  to  run  at  CUR_TIME  +  1/32th
  531.      seconds, while process #2 always schedules itself to run at CUR_TIME +
  532.      1/16 seconds.  One  would  therefore  expect  that  process  #1  would
  533.      execute  32  times  each  second, and that process #2 would execute 16
  534.      times each second.  If  the  CPU  is  lightly  loaded,  then  this  is
  535.      (almost)  true.   If,  however, the CPU is heavily loaded by the other
  536.      processes running under MPE, then processes #1 and #2 will undoubtedly
  537.      run  less  frequently  than their reschedule intervals would indicate.
  538.      Furthermore, both processes will execute the same number of  times  in
  539.      any  given  time  interval.  The exact frequency of repetition will be
  540.      determined by the degree of CPU loading and the  number  of  processes
  541.      running under MPE.
  542.  
  543.      The reason for this is as follows.  When the CPU  is  heavily  loaded,
  544.      processes  which schedule themselves a very short time into the future
  545.      will always be  overdue  when  the  scheduler  checks  them.   In  our
  546.      scenario,  this  means that processes #1 and #2 will always be overdue
  547.      by the time the scheduler gets a chance to check them in the run-list.
  548.      The result is that each process will be run exactly once for each pass
  549.      the scheduler makes through the run-list.
  550.  
  551.      Another way to understand the problem is to consider a simple graphics
  552.      process  whose  job is to move a "bullet" across the screen at a given
  553.      rate.  One way to do this would be to have the process move the bullet
  554.      a  pixel  or  so  and then reschedule itself for a short time into the
  555.      future.  The time for which the process reschedules  itself  could  be
  556.      given as the time the process was most recently executed (as stored in
  557.      CUR_TIME)  plus  some  incremental  time  chosen  to  give  a  desired
  558.      repetition rate for the process.  While this simple approach will work
  559.      well when the CPU is lightly loaded, it does not work as well when the
  560.      CPU is very heavily loaded.
  561.  
  562.      What happens when the CPU is heavily loaded is that processes may  sit
  563.      in  the  scheduler's  run-list  long past the time for which they were
  564.      scheduled, simply because the CPU  is  very  busy  running  the  other
  565.      processes  in  the  system.   Considering  the  "bullet" process, this
  566.      results in "lost" time, and causes the bullet on the  screen  to  slow
  567.      down.  The reason time is "lost" from the perspective of the bullet is
  568.      that being ignorant of absolute time (the time indicated by the system
  569.      clock)  the  process  always  reschedules  itself relative to when its
  570.      current invocation started, as indicated  by  CUR_TIME.   The  process
  571.      makes  no attempt to "catch-up" to where the bullet would have been if
  572.      the CPU had been lightly loaded.
  573.  
  574.      One solution to this problem is to schedule processes based not on the
  575.      time  value  stored  in  the  CUR_TIME  variable,  but rather based on
  576.      current time value in the system clock.  Doing this enables a  process
  577.      to  recover time that was lost to other processes.  In the case of our
  578.      "bullet" process, the process code could  examine  the  system  clock,
  579.      determine  how  much  real  time has passed since the process was last
  580.      invoked, and then move the bullet a number of pixels  consistent  with
  581.      the  desired  rate of travel.  The visual result on the screen in this
  582.      case is that the bullet has the desired average rate of travel  across
  583.      the  screen,  regardless of CPU loading.  As a side effect, the motion
  584.  
  585.                                                                     Page 11
  586.  
  587.  
  588.      of the bullet may appear a little "jittery" since the process may move
  589.      it  several  pixels in rapid succession during each invocation, rather
  590.      than  moving  it  one  pixel  per  invocation  as   in   the   simpler
  591.      implementation.
  592.  
  593.      The programmer should  be  very  careful,  however,  when  programming
  594.      processes  which  attempt  to  "catch  up" to the system clock in this
  595.      manner.  To see how this  type  of  process  definition  can  lead  to
  596.      problems,  consider  a heavily loaded system with two bullet processes
  597.      which run based on the absolute system time as described above.
  598.  
  599.      To facilitate the reading of the system clock, MPE includes a  routine
  600.      called  GET_TICKS  which  returns the current (16 nibble) value of the
  601.      system clock to register C.  This routine  modifies  register  A,  but
  602.      leaves all other processor registers intact.
  603.  
  604.      Examples of both implementations of the "bullet" process can  be  seen
  605.      in  the  demo processes included with the MPE distribution.  This demo
  606.      also illustrates the problems regarding the  relative  frequencies  of
  607.      process invocation in a heavily loaded system.
  608.  
  609.  
  610.  
  611.      3.9  Assembly-time Errors
  612.  
  613.      Because of the way MPE sets up its data structures  for  managing  the
  614.      processes,  it  is  necessary  that  the  processes  be defined by the
  615.      programmer  with  contiguous  process  IDs.   For  example,   if   the
  616.      programmer has defined process #4, then processes #1-#3 must also have
  617.      been defined.  If the programmer leaves any  "holes"  in  the  process
  618.      numbering scheme, MPE will generate an error message to that effect at
  619.      assembly-time.
  620.  
  621.  
  622.  
  623.      3.10  Run-time Errors
  624.  
  625.      There are two common errors that the programmer  might  be  likely  to
  626.      make when using MPE.  Both involve the use of the ADD_PROCESS routine.
  627.      The first  scenario  is  when  the  user  attempts  to  schedule  more
  628.      processes  than  there  are process slots in the scheduler's run-list.
  629.      The current implementation of STAR allows only 10 pending processes in
  630.      the run-list at a time.  If the user attempts to schedule more than 10
  631.      processes concurrently, MPE will push onto the stack an error code  of
  632.      99d,  indicating  that  the  run-list  overflowed,  and  the ID of the
  633.      process which made the call to ADD_PROCESS when the failure  occurred.
  634.      Both  of  these  values are pushed as short binary integers.  MPE then
  635.      halts execution of the machine language code and  continues  with  the
  636.      RPL thread.
  637.  
  638.      The size of the  run_list  can  be  easily  increased  to  accommodate
  639.      applications which need more than 10 processes scheduled concurrently.
  640.      Refer to the section below on "MPE Customization".
  641.  
  642.      The other potential misuse of  ADD_PROCESS  is  when  the  user  calls
  643.  
  644.                                                                     Page 12
  645.  
  646.  
  647.      ADD_PROCESS  with  an  invalid  process  ID.   I  did  this once while
  648.      debugging MPE and my machine  promptly  crashed  with  "memory  lost".
  649.      Very unforgiving.
  650.  
  651.      Therefore, MPE now checks the ID passed into ADD_PROCESS to make  sure
  652.      it  corresponds  to one of the processes defined by the user.  If not,
  653.      then MPE pushes an error code of 98d onto the stack as a short binary,
  654.      indicating  that  an invalid ID was passed into ADD_PROCESS.  MPE then
  655.      also pushes as short binaries the  ID  of  the  process  which  called
  656.      ADD_PROCESS,  and  the  erroneous  ID,  in that order.  MPE then halts
  657.      execution of the machine language code  and  continues  with  the  RPL
  658.      thread.
  659.  
  660.  
  661.  
  662.      3.11  MPE Customization
  663.  
  664.      The current  implementation  of  MPE  will  allow  up  to  20  defined
  665.      processes.   The user should note, however, that the version of MPE in
  666.      this distribution will only allow  as  many  as  10  processes  to  be
  667.      concurrently  scheduled.   This  has been done to optimize performance
  668.      for the set  of  process  definitions  which  comprise  the  MPE  demo
  669.      application.   This can easily be changed by the programmer to hold as
  670.      many processes as you need.  Read on.
  671.  
  672.      The scheduler scans through  the  run  list  in  a  circular  fashion,
  673.      updating  its  copy  of  the  system  time  once  per pass through the
  674.      run-list.  As the scheduler does  a  pass  through  the  run-list,  it
  675.      checks  the  time stamp of any valid processes against its copy of the
  676.      system time and runs the associated  process  if  its  time  stamp  is
  677.      earlier  than  the current system time.  Because of this circular scan
  678.      algorithm, it is highly advisable to limit the size of the run-list to
  679.      the   number  of  processes  which  the  programmer  expects  to  have
  680.      concurrently scheduled.  In this way, the  scheduler  does  not  waste
  681.      valuable CPU time checking run-list slots which will never be filled.
  682.  
  683.      The run-list is a static data structure  located  at  the  STAR  label
  684.      "RUN_LIST:".   The  programmer should increase or decrease the size of
  685.      this data allocation as needed, being sure to update the RUN_LIST_SIZE
  686.      variable at the same time.  (RUN_LIST_SIZE is a STAR variable which is
  687.      defined just above where the RUN_LIST is located in the MPE  sources.)
  688.      For each new slot in the RUN_LIST, the programmer should add a "DATA.B
  689.      0" to hold the process ID and then a "DATA.W 0" to  hold  the  process
  690.      time  stamp.  The RUN_LIST_SIZE variable indicates the number of slots
  691.      in RUN_LIST.
  692.  
  693.      If the current maximum of 20 processes is  insufficient  for  a  given
  694.      application,  then  the programmer should add lines of code to the MPE
  695.      data structure and initialization sections which appears  just  before
  696.      and  after  the  START:  label.  The lines which will need to be added
  697.      should be fairly obvious, and will have the form of:
  698.  
  699.                                                                     Page 13
  700.  
  701.  
  702.  
  703.      (appearing just before the START: label)
  704.  
  705.              GEN_HEADERS 21
  706.              GEN_HEADERS 22
  707.              .
  708.              .
  709.              .
  710.  
  711.              GEN_DESCRIPTOR 21
  712.              GEN_DESCRIPTOR 22
  713.              .
  714.              .
  715.              .
  716.  
  717.      (...and then appearing just after the START: label)
  718.  
  719.              HEADER_INIT 21 
  720.              HEADER_INIT 22
  721.              .
  722.              .
  723.              .
  724.       
  725.  
  726.  
  727.  
  728.                                     Figure 6
  729.  
  730.  
  731.      The new lines must be added to the end of each section (after the line
  732.      corresponding  to  process  #20).   You  will  need  to  make  similar
  733.      additions in the FILL_DESCRIPTORS macro, adding more "FILL  xx"  lines
  734.      for  the  additional  processes.  The need to make the above additions
  735.      will be removed in a later version of MPE.
  736.  
  737.      If anyone needs more than 20 processes, please send mail.  I  want  to
  738.      see your application :-)
  739.  
  740.  
  741.  
  742.      3.12  Assembler Considerations
  743.  
  744.      Due  to  limitations  in  the  current  version  of  Star   (V1.04.4),
  745.      applications   running   under   MPE  should  be  assembled  with  the
  746.      jumpification feature turned off.  This is done by using the -j switch
  747.      on  the  command  line  when  STAR  is  run.   For help on the various
  748.      switches which can be used on the STAR command line, invoke STAR  with
  749.      the  -h  switch,  which will cause STAR to print out its standard help
  750.      text.
  751.  
  752.                                                                     Page 14
  753.  
  754.  
  755.      4  MPE DEMO PROCESSES
  756.  
  757.      Included with this distribution of MPE  is  a  set  of  seven  process
  758.      definitions   which  demonstrate  the  basic  features  of  MPE.   The
  759.      following sections will describe briefly what each of these  processes
  760.      do and how they interact.
  761.  
  762.  
  763.  
  764.      4.1  Application Overview
  765.  
  766.      This demo application is a simple demonstration of how MPE can be used
  767.      to  do  graphical  animation of several objects on the screen at once.
  768.      This also provides a good visual  demonstration  of  the  CPU  loading
  769.      effects described in earlier sections.
  770.  
  771.      Basically,  the  application  consists  of  a  fire  button  and  four
  772.      "bullets".   This  application uses the [+] button as the fire button.
  773.      When the user starts the application, the screen blanks and waits  for
  774.      the  user to press the fire button.  For each press of the [+] button,
  775.      the application will fire a bullet.  The bullets start  at  the  upper
  776.      left  of  the  screen and move down the screen, row by row, until they
  777.      reach the bottom of the screen.  When any  bullet  reaches  the  lower
  778.      right  of  the screen, the application stops.  When the user has fired
  779.      all four bullets, the fire button is disabled for the remainder of the
  780.      application.   The  [.]  key  quits  the  application.   Refer  to the
  781.      following section for how to download and run the application.
  782.  
  783.  
  784.  
  785.      4.2  Downloading And Running The Application
  786.  
  787.      Included in this distribution is  the  uuencoded  binary  file.   This
  788.      binary  file is a directory containing the machine language code which
  789.      makes up the MPE application, and a short RPL program which  runs  it.
  790.      The  user  should  uudecode  this file and then download the resulting
  791.      binary file in the usual manner to the HP48SX (make sure  to  set  the
  792.      HP48SX  kermit  to  binary  mode.  The RPL program, "DEMO", in the new
  793.      directory serves only to clear the display and set the display to PICT
  794.      memory  before  running the machine language application ("XXX").  You
  795.      should _not_ run the "XXX" program directly, since if PICT memory  has
  796.      been  purged (by doing a PICT PURGE), then the application may corrupt
  797.      memory.  Once started by  pressing  the  button  labeled  "DEMO",  the
  798.      machine  language  program  can be aborted at any time by pressing the
  799.      [.] button.
  800.  
  801.  
  802.  
  803.      4.3  Process Descriptions
  804.  
  805.      The following sections describe each of the seven processes which make
  806.      up the application.  Note that processes #1 and #2 are not integral to
  807.      the application and could be omitted.
  808.  
  809.                                                                     Page 15
  810.  
  811.  
  812.      4.3.1  Process #1
  813.  
  814.      This is the process which is auto-executed  by  MPE  at  startup.   In
  815.      terms  of the application, the only thing that this process does is to
  816.      start up the keyboard scanner process  (#7).   Another  function  that
  817.      this  process  performs,  in  conjunction  with  process #2 is to do a
  818.      register context integrity test periodically while the application  is
  819.      running.  This was written mainly for debugging MPE, but may be useful
  820.      to the programmer in general, so I have left it in.  It runs once  per
  821.      second, so it does not add significant overhead to the application.
  822.  
  823.  
  824.  
  825.      4.3.2  Process #2
  826.  
  827.      This process does nothing except clear all  the  processor  registers.
  828.      The  purpose of this process is to work with process #1 to implement a
  829.      register context integrity test.
  830.  
  831.  
  832.  
  833.      4.3.3  Processes #3, #4, And #5.
  834.  
  835.      These processes are grouped together here because they share the  same
  836.      process  definition.   This process definition shows a good example of
  837.      how to share code between two or more processes.  The  code  for  this
  838.      process  definition  makes  use of the IF_PROC/ENDIF_PROC macros to do
  839.      conditional execution of certain sections of code based on the current
  840.      context ID.  For example, the three bullets owned by processes #3, #4,
  841.      and #5 all move at different speeds on the screen.  This was done here
  842.      to  visually  show the independence of the bullet processes running in
  843.      the system.  The process definition uses the IF_PROC/ENDIF_PROC macros
  844.      to  schedule  the  correct  process  at  the  correct  interval,  thus
  845.      determining the speed of each bullet.
  846.  
  847.      One thing to note is that this process definition does not attempt  to
  848.      control the speed of a bullet with great accuracy.  In other words, as
  849.      the four bullets are fired and the CPU becomes  more  heavily  loaded,
  850.      the  speed  of  the  bullets corresponding to processes #3, #4, and #5
  851.      will slow down slightly.  This  is  due  to  process  latency  in  the
  852.      scheduler.   Process  #6 shows a similar bullet process, but one which
  853.      keeps track of absolute time and maintains an accurate velocity on the
  854.      screen, independent of CPU loading.
  855.  
  856.  
  857.  
  858.      4.3.4  Process #6
  859.  
  860.      This  process  definition  is  very  similar  to  that  which  defines
  861.      processes #3, #4,and #5.  The main difference is that process #6 keeps
  862.      track of absolute time, making sure that its bullet moves  at  exactly
  863.      the  velocity  indicated in the process definition.  In this way, even
  864.      though the bullet for this process does not start  until  the  CPU  is
  865.      already  very  busy with the other three bullets, it will maintain its
  866.      velocity at the expense of the other three bullets.
  867.  
  868.                                                                     Page 16
  869.  
  870.  
  871.      The effects of the use  of  absolute  time  in  this  process,  versus
  872.      relative  time in the other three bullet processes, can be seen in two
  873.      ways.  The first way is that after firing the four bullets, it  should
  874.      be  discernible  that  the  fourth  bullet (process #6) moves slightly
  875.      faster than the second one (process #4).  By examining the definitions
  876.      of these processes, one quickly sees that processes #4 and #6 have the
  877.      same rescheduling interval.  The  difference  in  speed  is  therefore
  878.      attributable  to  the  time that process #4 loses while sitting in the
  879.      RUN_LIST waiting to be run by the scheduler.  Since process  #6  keeps
  880.      track  of  absolute  time  internal  to  itself,  this queuing time is
  881.      accurately recovered each time the process is invoked.
  882.  
  883.      The second way that the use of absolute time in process #6 can be seen
  884.      is  by  pressing  and briefly holding the [ON] key.  Pressing this key
  885.      temporarily halts execution of the calculator, causing  all  processes
  886.      to pause.  When the key is released, however, the bullet of process #6
  887.      will quickly "zip" ahead to catch up to where it should be.  A similar
  888.      effect  can  also  be  seen  by  pressing  and  holding some other key
  889.      (besides  [ON]).   Since  the  keyboard  controller  causes  lots   of
  890.      interrupts  in when this is done, it has the effect of heavily loading
  891.      the CPU.  What can be seen on the display is that while the user holds
  892.      down  a  key and thereby loads the CPU, the three bullets which do not
  893.      use absolute timing slow down noticably, while the bullet for  process
  894.      #6  maintains  its  correct  average  velocity.   The  manner in which
  895.      process #6 "catches up" to where it should  be  can  be  seen  in  the
  896.      bullet's jumpiness.
  897.  
  898.  
  899.  
  900.      4.3.5  Process #7
  901.  
  902.      This process comprises the  keyboard  scanner.   This  process  simply
  903.      checks the keybuffer for the [+] key, and if it finds it then the next
  904.      bullet process is scheduled for execution.  If the [.] key is pressed,
  905.      this  process will cause the application to exit and continue with the
  906.      RPL thread.  This process runs approximately 8 times per second.
  907.  
  908.